搜狐新闻客户端Android端侧双擎Hybrid AI框架探索
本文字数:2917字
预计阅读时间:24分钟
前言
人工智能,深度学习,机器学习,当今已经很广泛的应用到了手机端APP。无论是各类修图软件上的各种抠图美化,实时变装。还是社交软件的图片自动匹配文案。抑或是输入法程序和微信上的语音文字互转。以及各类消费应用上的人工智能客服,高效收集和处理客户的反馈。
——这些都用到了端侧人工智能机器学习的解决方案。
这个简单功能,为用户发的内容带来了更多的曝光机会,同时为话题运营也创造了更多的内容,一举两得。
那么,怎样实现类似的功能呢?
实现这个功能,传统的三种方案是这样的:
• 方案1.首先,客户端采集压缩图片,做预处理。然后上传图片到云端存储,云存储再利用AI服务器算力,使用物体识别,物体分类等成熟的模型识别图片中的物体,再匹配合适的文案。最后下发到客户端。
• 方案2.客户端自己训练实现一个端侧模型,大多利用类似Tensor Flow Lite,Paddle Paddle之类的支持Arm架构 cpu芯片的端侧AI框架,使用内置到应用内部的模型。需要识别物体时,由App加载AI引擎,引擎再加载预先训练好的模型,模型推演出结果以后,在手机端完成识别和展示。
• 方案3.客户端在不同系统的手机上,利用手机系统内置的AI能力,如华为手机上的EMUI内置的HiAI,调用系统接口。通常内置一些AI能力的手机,都有系统级的最优算力方案,可以调度NPU,而不是使用CPU或者GPU,可以更快速的利用系统算力完成识别,再在应用端做结果展示。
优点整合的Hybrid AI Engine
在这个大前提下,搜狐新闻hybrid双擎AI客户端引擎框架应运而生。框架把常用的AI功能作为独立的可扩展模块,提供给上层应用开发者。利用Tensor Flow Lite和HiAI双引擎,做出决策,再由Hybrid AI Engine由速度优先,结果优先,性能优先几种模式做决策整合纠偏,最终提供给应用层推理的结果。
架构思路如下图所示
Talk is cheap, show me the code. 实战演示
光说不练假把式,让我们回到最初提出的例子,来实现一下识图功能。
实际的应用开者,把双擎Hybrid AI引擎编译集成到app端以后,只需要再加入下面几行代码,就可以实现从图片Bitmap中获取对应文案的功能:
AIHelperFactory aiHelperFactory =
AIHelperFactory.getInstance(context);
aiHelperFactory.init(AIHelperFactory.AI_TOOL_RECOGNIZER);
……
if (AIHelperFactory.getInstance(context).isReady(AIHelperFactory.AI_TOOL_RECOGNIZER)) {
discription = AIHelperFactory.getInstance(context).getRecognizer().recognize(recognizeBitmap);
}
…… AIHelperFactory.getInstance(context).release(AIHelperFactory.AI_TOOL_RECOGNIZER);
可以看到这个调用封装十分精简,中间的模型加载和推算已经对开发者完全透明,假如使用TensorFlowLite完成同样的工作,需要下面这段冗长的代码:
private void initModel(Context context) {
final Context appContext = context.getApplicationContext();
ModelControler.getInstance(appContext).prepareModels(TF_MODEL_NAME, new ModelControler.ModelDownloadListener() {
@Override
public void onModelReady() {
String modelPath =
ModelControler.getInstance(appContext).getModelPath(TF_MODEL_NAME);
recreateClassifier(appContext, modelPath);
}
@Override
public void onModelDownloadFailed() {
// TODO: retry?
}
});
}
public void recreateClassifier(Context context, String modelPath) {
if (mClassifier != null) {
LOGGER.d("Closing mClassifier.");
mClassifier.close();
mClassifier = null;
}
try {
mClassifier =
TFLiteObjectDetectionAPIModel.create(
modelPath + TF_OD_API_MODEL_FILE,
modelPath + TF_OD_API_LABELS_FILE,
mInputSize,
TF_OD_API_IS_QUANTIZED);
mRecognizedBitmap =
Bitmap.createBitmap(
mInputSize, mInputSize, Bitmap.Config.ARGB_8888);
} catch (final IOException e) {
e.printStackTrace();
}
}
private List<Recognition> recognizeQualify(Bitmap sourceBitmap) {
// NOTICE: this should be invoked in working thread
if (mClassifier == null || mRecognizedBitmap == null) {
return null;
}
Matrix frameToCropTransform =
ImageUtils.getTransformationMatrix(
sourceBitmap.getWidth(),
sourceBitmap.getHeight(),
mInputSize,
mInputSize,
0,
true);
Matrix cropToFrameTransform = new Matrix();
frameToCropTransform.invert(cropToFrameTransform);
final Canvas canvas = new Canvas(mRecognizedBitmap);
canvas.drawBitmap(sourceBitmap, frameToCropTransform, null);
List<Recognition> results = mClassifier.recognizeImage(mRecognizedBitmap);
List<Recognition> qualifyResults = new ArrayList<>();
if (results.size() > 0) {
for (Recognition result : results) {
if (result.getConfidence() >= MINIMUM_CONFIDENCE_TF_OD_API) {
qualifyResults.add(result);
}
}
return qualifyResults;
} else {
return null;
}
}
@Override
public String recognize(Bitmap sourceBitmap) {
List<Recognition> results = recognizeQualify(sourceBitmap);
List<String> labels = new ArrayList<>();
for (Recognition result : results) {
labels.add(mTFTranslater.translate(result));
}
return mDescriptionCreator.getDescription(labels);
}
那么这精简调用的背后,详细是怎么实现的呢?
首先,根据需求初始化双擎AI框架,在收到客户端请求之后,会优先检查系统端有不有相近的系统级实现,如果有,会先调用系统接口,快速的返回结果。
接着会使用Tensor Flow Lite,加载对应功能的处理模型,使用客户端侧返回的结果,对系统返回结果进行补充或者纠偏。
public static AIHelperFactory getInstance(Context context) {
if (sInstance != null) {
return sInstance;
}
sInstance = new AIHelperFactory(context);
return sInstance;
}
public void init(final int... tools) {
if (HiAIUtils.isHuaweiAIAppInstalled(mContext)) {
VisionBase.init(mContext, new ConnectionCallback() {
@Override
public void onServiceConnect() {
for (int tool : tools) {
createTools(tool);
}
}
@Override
public void onServiceDisconnect() {
release();
}
});
} else {
for (int tool : tools) {
createTools(tool);
}
}
}
TFlite的模型还支持在线下载和更新。模型在线更新的机制,可以更好的缩窄应用场景,优化模型体积,同时更好的配合细化场景运营。
同样是“AI识图”的例子,用户不同时间上传同一张“山水”的风景图片,我们可以配合当前客户端正在运营的活动,配以不同的AI识图模型,给用户返回不同的结果,可以是#无人机大赛#相关的,也可以是#小长假旅游风景照#。做到差异化,精细化的为实际项目引流的作用。
接着,应用端只用直接调用初始化好的功能模块提供的识别图片方法,获取图片内容:
public Recognizer(Context context) {
if (HiAIUtils.isHuaweiAIAppInstalled(context)) {
mApi = new RecognizerHiAI(context);
} else {
mApi = new RecognizerTF(context);
}
}
public String recognize(Bitmap bitmap) {
if (mApi != null) {
return mApi.recognizeTarget(bitmap);
}
return null;
}
从应用端只看到一个api的interface,实际的实现,可能是两种,HiAI或者Tensor Flow,各自实现了对应模块的功能接口:
public class RecognizerHiAI extends RecognizerApi {
……
@Override
protected String recognizeTarget(Bitmap sourceBitmap) {
if (!isReady()) {
return null;
}
VisionImage vi = VisionImage.fromBitmap(sourceBitmap);
Scene sceneResult = new Scene(Scene.UNKNOWN);
int result = mSceneDetector.detect(vi, sceneResult, null);
if (result != 0) {
Log.e(TAG, "detect failed:" + result);
return null;
}
return mHiAITranslater.translate(sceneResult.getType());
}
……
}
public class RecognizerTF extends RecognizerApi {
……
private List<Recognition> recognizeQualify(Bitmap sourceBitmap) {
// NOTICE: this should be invoked in working thread
if (mClassifier == null || mRecognizedBitmap == null) {
return null;
}
Matrix frameToCropTransform =
ImageUtils.getTransformationMatrix(
sourceBitmap.getWidth(),
sourceBitmap.getHeight(),
mInputSize,
mInputSize,
0,
true);
Matrix cropToFrameTransform = new Matrix();
frameToCropTransform.invert(cropToFrameTransform);
final Canvas canvas = new Canvas(mRecognizedBitmap);
canvas.drawBitmap(sourceBitmap, frameToCropTransform, null);
List<Recognition> results = mClassifier.recognizeImage(mRecognizedBitmap);
List<Recognition> qualifyResults = new ArrayList<>();
if (results.size() > 0) {
for (Recognition result : results) {
if (result.getConfidence() >= MINIMUM_CONFIDENCE_TF_OD_API) {
qualifyResults.add(result);
}
}
return qualifyResults;
} else {
return null;
}
}
……
}
最后,识别结束,如果不需要再识别的话,可以调用释放资源接口。释放加载到内存的模型文件,完成识别。
public void release(final int... tools) {
for (int toolId : tools) {
if (toolId == AI_TOOL_RECOGNIZER || toolId == AI_TOOL_ALL) {
if (mRecognizer != null) {
mRecognizer.release();
mRecognizer = null;
}
}
if (toolId == AI_TOOL_ID_DETECTOR || toolId == AI_TOOL_ALL) {
if (mIDCardDetector != null) {
mIDCardDetector.release();
mIDCardDetector = null;
}
}
if (toolId == AI_TOOL_FACE_COMPARE || toolId == AI_TOOL_ALL) {
if (mFaceCompare != null) {
mFaceCompare.release();
mFaceCompare = null;
}
}
………
}
}
Last but not least未来展望
客户端架构选择使用了TFlite的模型,算法工程师可以方便的把自己各种架构的模型转换成Tensor Flow Lite兼容模式。这样算法和客户端可以做到完美的解耦合,让各个领域的专家从事他们所擅长的工作,提高效率,中间的自动适配和翻译工作交给框架来维护。
除了图片识别这类基础功能,双擎AI框架还已经实现了:人脸识别,人脸比较,证件识别,语音输入…并且,各个模块完全独立,可以实时扩展更新增加新的模块。基础SDK包只有100K左右的体积,其他模块都可以通过在线下载的方式支持。对于apk本身的负担极小。
Hybrid AI Engine是端侧AI方案的一次整合,目前还在beta版本迭代开发中,这是一个开发友好度很好的支持库,应用端工程师可以不用深入了解人工智能机器学习的相关知识,也可以通过我们提供好的接口,快速的开发炫目的让人眼前一亮的“智能”的功能。真正release 1.0版本之后,我们打算彻底开源整个项目,欢迎届时大家一起来开发更多的更有意思的扩展模块。
也许你还想看
(▼点击文章标题或封面查看)
2021-01-14
2021-01-07
2020-12-10
2020-12-03
2020-11-26
加入搜狐技术作者天团
千元稿费等你来!
👈 戳这里!